﻿/*
   Copyright 2012 Dmitry Bratus

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Flower.Services;
using Flower.Services.Data;
using Jurassic.Library;
using Jurassic;
using Flower.Directory.Scripting.DirectoryEntries;

namespace Flower.Directory.Scripting
{
    public class DirectoryObject : ObjectInstance
    {
        private readonly IDirectory _directory;

        public IDirectory Directory { get { return _directory; } }

        public DirectoryObject(ScriptEngine engine, IDirectory directory)
            : base(engine)
        {
            PopulateFunctions();

            _directory = directory;
        }

        [JSFunction(Name = "getPathById")]
        public string GetPathById(object id)
        {
            try
            {
                return _directory.GetPathById(JsConvert.ObjectToString(id));
            }
            catch(Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message, ex);
            }
        }

        [JSFunction(Name = "get")]
        public object Get(object what)
        {
            try
            {
                DirectoryEntry result = _directory.Get(JsConvert.ObjectToString(what));
                return (result != null) ? (object)JsConvert.EntryToObject(Engine, result) : Null.Value;
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message, ex);
            }
        }

        [JSFunction(Name = "getInfo")]
        public object GetInfo(object what)
        {
            try
            {
                DirectoryEntry result = _directory.GetInfo(JsConvert.ObjectToString(what));
                return (result != null) ? (object)JsConvert.EntryToObject(Engine, result) : Null.Value;
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message, ex);
            }
        }

        [JSFunction(Name = "getChildren")]
        public ArrayInstance GetChildren(object parent, int entryTypes, int property, int order, int offset, int limit)
        {
            try
            {
                ArrayInstance arr = Engine.Array.New();

                var children =
                    _directory
                        .GetChildren
                        (
                            JsConvert.ObjectToString(parent),
                            (DirectoryEntryTypes)entryTypes,
                            (SortProperty)property,
                            (SortOrder)order,
                            offset,
                            limit
                        )
                        .Select(ent => JsConvert.EntryToObject(Engine, ent));

                foreach (DirectoryEntryObject child in children)
                {
                    arr.Push((object)child);
                }

                return arr;
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message, ex);
            }
        }

        [JSFunction(Name = "getChildrenInfo")]
        public ArrayInstance GetChildrenInfo(object parent, int entryTypes, int property, int order, int offset, int limit)
        {
            try
            {
                ArrayInstance arr = Engine.Array.New();

                var children =
                    _directory
                        .GetChildrenInfo
                        (
                            JsConvert.ObjectToString(parent),
                            (DirectoryEntryTypes)entryTypes,
                            (SortProperty)property,
                            (SortOrder)order,
                            offset,
                            limit
                        )
                        .Select(ent => JsConvert.EntryToObject(Engine, ent));

                foreach (DirectoryEntryObject child in children)
                {
                    arr.Push((object)child);
                }

                return arr;
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message, ex);
            }
        }

        [JSFunction(Name = "countChildren")]
        public int CountChildren(object parent, int entryTypes)
        {
            try
            {
                return (int)_directory.CountChildren(JsConvert.ObjectToString(parent), (DirectoryEntryTypes)entryTypes);
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message, ex);
            }
        }

        [JSFunction(Name = "getAncestor")]
        public object GetAncestor(object child, int n, int ancestorEntryTypes)
        {
            try
            {
                DirectoryEntry result = _directory.GetAncestor(JsConvert.ObjectToString(child), n, (DirectoryEntryTypes)ancestorEntryTypes);
                return (result != null) ? (object)JsConvert.EntryToObject(Engine, result) : Null.Value;
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message, ex);
            }
        }

        [JSFunction(Name = "getAncestorInfo")]
        public object GetAncestorInfo(object child, int n, int ancestorEntryTypes)
        {
            try
            {
                DirectoryEntry result = _directory.GetAncestorInfo(JsConvert.ObjectToString(child), n, (DirectoryEntryTypes)ancestorEntryTypes);
                return (result != null) ? (object)JsConvert.EntryToObject(Engine, result) : Null.Value;
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message, ex);
            }
        }

        [JSFunction(Name = "save")]
        public string Save(object parent, ObjectInstance entry)
        {
            try
            {
                return _directory.Save(JsConvert.ObjectToString(parent), JsConvert.ObjectToEntry(entry));
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message, ex);
            }
        }

        [JSFunction(Name = "move")]
        public void Move(object what, object where)
        {
            try
            {
                _directory.Move(JsConvert.ObjectToString(what), JsConvert.ObjectToString(where));
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message, ex);
            }
        }

        [JSFunction(Name = "remove")]
        public void Remove(object what)
        {
            try
            {
                _directory.Remove(JsConvert.ObjectToString(what));
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message, ex);
            }
        }

        [JSFunction(Name = "exists")]
        public bool Exists(object what, object timestamp)
        {
            try
            {
                DateTime timestampDateTime;

                if (timestamp == null || timestamp is Null || timestamp is Undefined)
                {
                    timestampDateTime = DateTime.MinValue;
                }
                else if (timestamp is DateTime)
                {
                    timestampDateTime = (DateTime)timestamp;
                }
                else
                {
                    var timestampDateInstance = timestamp as DateInstance;

                    if (timestampDateInstance != null)
                    {
                        timestampDateTime = timestampDateInstance.Value;
                    }
                    else
                    {
                        throw new ArgumentException(string.Format("Argument timestamp has invalid type {0}.", timestamp.GetType().FullName), "timestamp");
                    }
                }

                return _directory.Exists(JsConvert.ObjectToString(what), timestampDateTime);
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message, ex);
            }
        }
    }
}
